<?php

namespace Common\Model;

use Think\Model\RelationModel;
use Think\Hook;
use Org\Util\ArrayToolkit;

import('Common.Lib.ModelTrait');

class UserModel extends RelationModel
{
    use \ModelTrait; // 继承扩展方法
    private $client_sign = array('id', 'username', 'nickname', 'password'); // 客户端通信
    public $login_expire = 0;
    protected $delete_list = [];
    protected $update_list = [];
    protected $num2text = array(
        'status' => array(
            0 => '禁用',
            1 => '正常',
        )
    );
    // 关联配置
    protected $_link = array(
        'UserGroupAccess' => array(
            'mapping_type' => self::HAS_ONE,
            'foreign_key' => 'uid',
            'mapping_key' => 'id'
        ),
    );
    protected $_auto = array(
//        array('password', 'pwdHash', self::MODEL_BOTH, 'callback'),
        array('reg_time', NOW_TIME, self::MODEL_INSERT),
        array('reg_ip', 'get_client_ip', self::MODEL_INSERT, 'function'/*, 1*/),
        array('update_time', NOW_TIME),
        array('status', 'getStatus', self::MODEL_BOTH, 'callback'),
        ['avatar', 'img_base64', self::MODEL_BOTH, 'callback'],
    );
    protected $_validate = array(
        array('username', 'require', '用户名不能为空！'),
        array('username', '', '用户名已经存在！', 0, 'unique'),
        array('username', 'checkUsername', '用户名已经存在！', 0, 'callback'),
        array('username', 'checkDenyMember', '用户名禁止注册', self::EXISTS_VALIDATE, 'callback'), //用户名禁止注册
        /* 验证邮箱 */
        array('email', 'email', '邮箱格式不正确', self::EXISTS_VALIDATE), //邮箱格式不正确
        array('email', '1,32', '邮箱长度不合法', self::EXISTS_VALIDATE, 'length'), //邮箱长度不合法
        array('email', 'checkDenyEmail', '邮箱禁止注册', self::EXISTS_VALIDATE, 'callback'), //邮箱禁止注册
        array('email', '', '邮箱被占用', self::VALUE_VALIDATE, 'unique'), //邮箱被占用

        /* 验证手机号码 */
        array('phone', 'checkPhone', '手机格式不正确', self::EXISTS_VALIDATE, 'callback'), //手机格式不正确
        array('phone', 'checkDenyMobile', '手机禁止注册', self::EXISTS_VALIDATE, 'callback'), // 手机禁止注册
        array('phone', '', '手机号被占用', self::VALUE_VALIDATE, 'unique'), //手机号被占用

        array('reg_ip', 'checkDenyIp', 'IP禁止注册', self::EXISTS_VALIDATE, 'function'),// 验证ip是否禁止注册
        array('password', 'require', '请设置密码！', self::EXISTS_VALIDATE, '', self::MODEL_INSERT),
    );

    public function _initialize()
    {
        $this->login_expire = 86400 * 30; // 30天过期
    }

    public function isAdministrator($uid)
    {
        $administrator_user = C('ADMINISTRATOR_USER');
        if (is_numeric($administrator_user)) {
            return $uid == $administrator_user;
        } else if (is_array($administrator_user)) {
            return in_array($uid, $administrator_user);
        }
        return false;
    }

    public function register($username, $password, $phone = '', $email = '')
    {
        $data = array(
            'username' => $username,
            'password' => $password,
        );
        $phone && $data['phone'] = $phone;
        $email && $data['email'] = $email;

        if ($data = $this->create($data)) {
            $data['UserGroupAccess']['group_id'] = C('USER_DEFAULT_GROUP'); // 关联数据
            Hook::listen('register_user', $data);
            $res = $this->relation(true)->add($data);
            return $res;
        }
        return false;
    }

    /**
     * 关联删除
     * @param $uid
     * @return mixed
     */
    public function delUser($uid)
    {
        $map = array(
            'id' => $uid,
        );
        return $this->relation(true)->where($map)->delete();
    }

    /**
     * 设置用户的角色
     * @param $uid
     * @param $group
     * @return bool|int|mixed|string
     */
    public function setUserGroup($uid, $group)
    {
        if (!$uid || !$group) {
            return false;
        }
        $data = array(
            'uid' => $uid,
            'group_id' => $group,
        );
        return D('UserGroupAccess')->add($data, array(), true);
    }

    public function getUserGroup($uid)
    {
        if (!$uid) return false;
        $row = D('Common/UserGroupAccessView')->where(array('uid' => $uid))->field(true)->find();
        return $row['group_name'];
    }

    /**
     * 登录需求：客户端有会话并且服务器也有对应会话
     * 可以使用本地与服务器的会话关系进行强制用户下线
     */
    public function isLogin()
    {
        $model = D('UserSession');
        if (session('icy8_user_auth') && session('icy8_user_auth_sign')) {
            $user_auth = session('icy8_user_auth');
            $user_auth_sign = session('icy8_user_auth_sign');
        } else if (cookie('icy8_user_auth') && cookie('icy8_user_auth_sign')) {
            $user_auth = cookie('icy8_user_auth');
            $user_auth_sign = cookie('icy8_user_auth_sign');
        } else {
            return false;
        }
        $uid = $user_auth['id'];
        $map = array(
            'uid' => $uid,
            'value' => $user_auth_sign,
            'expire' => array('gt', NOW_TIME),
        );
        // 是否允许异地登录
        if (!C('REMOTE_LOGIN_ALLOW'))
            $map['ip'] = get_client_ip(); // 不允许异地登录
        $session = $model->where($map)->count(); // 拥有会话
        if (!$session) {
            // 服务器会话不存在 清除所有本地会话
            $this->clearLocalSession();
            return false;
        }
        return true;
    }

    /**
     * 获取本地会话信息
     */
    public function getLocalSession()
    {
        if (session('icy8_user_auth') && session('icy8_user_auth_sign')) {
            $user_auth = session('icy8_user_auth');
        } else if (cookie('icy8_user_auth') && cookie('icy8_user_auth_sign')) {
            $user_auth = cookie('icy8_user_auth');
        } else {
            return false;
        }
        return $user_auth;
    }

    public function getMe()
    {
        $user_auth = $this->getLocalSession();
        if ($user_auth['id']) {
            return D('Common/UserView')->find($user_auth['id']);
        }
        return false;
    }

    public function pwdHash($p)
    {
        return password_hash($p, PASSWORD_DEFAULT); // 制作密码散列
    }

    /**
     * 验证哈希
     * @param $p
     * @param $hash
     * @return bool
     */
    public function pwdVerify($p, $hash)
    {
        return password_verify($p, $hash);
    }

    /**
     * 用户验证
     * @param $username
     * @param $password
     * @return bool
     */
    public function userVerify($username, $password)
    {
        $map = array(
            'username|email|phone|id' => $username
        );
        $find = $this->where($map)->find();
        $origin_hash = $find['password']; // 数据库提取password_hash字串
        $find['password'] = $password;
        $find['password_hash'] = $this->pwdHash($password);
        $res = $find;
        if (!$find) {
            $this->error = '用户不存在';
            $res = false;
        } else if (!$this->pwdVerify($password, $origin_hash)) {
            // 用户输入的明文密码和加密串对比
            $this->error = '用户和密码不匹配';
            $res = false;
        }
        Hook::listen('user_verify', $res);
        return $res;
    }

    public function getSign($username, $password)
    {
        $user = $this->userVerify($username, $password);
        if (!$user)
            return false;
        $user_auth = ArrayToolkit::field($this->client_sign, $user);
        $user_auth_sign = data_auth_sign($user_auth); // 客户端散列
        return array(
            'user_auth' => $user_auth,
            'user_auth_sign' => $user_auth_sign,
            'user_info' => $user,
        );
    }

    /**
     * session登录
     * @param $username
     * @param $password
     * @return bool
     */
    public function sessionLogin($username, $password)
    {
        $sign = $this->getSign($username, $password);
        if (!$sign)
            return false;
        $this->clearLocalSession();// 先清除本地会话
        session('icy8_user_auth', $sign['user_auth']);
        session('icy8_user_auth_sign', $sign['user_auth_sign']);
        return $this->setSession($sign['user_info'], $sign['user_auth_sign']); // 服务器通信
    }

    /**
     * cookie登录
     * @param $username
     * @param $password
     * @return bool
     */
    public function cookieLogin($username, $password)
    {
        $sign = $this->getSign($username, $password);
        if (!$sign)
            return false;
        $options = array(
            'expire' => $this->login_expire,
        );
        $this->clearLocalSession();// 先清除本地会话
        cookie('icy8_user_auth', $sign['user_auth'], $options);
        cookie('icy8_user_auth_sign', $sign['user_auth_sign'], $options);
        return $this->setSession($sign['user_info'], $sign['user_auth_sign']); // 服务器通信
    }

    public function clearLocalSession()
    {
        // 清除所有本地会话
        session('icy8_user_auth', null);
        session('icy8_user_auth_sign', null);
        cookie('icy8_user_auth', null);
        cookie('icy8_user_auth_sign', null);
    }

    public function setSession($user, $hash, $type = 'login')
    {
        if (!$user) return false;
        $data = array(
            'uid' => $user['id'],
            'name' => $user['username'],
            'value' => $hash,
            'ip' => get_client_ip(),
            'expire' => NOW_TIME + $this->login_expire,
            'create_time' => NOW_TIME,
            'type' => $type,
        );
        $model = D('UserSession');
        if (!$model->add($data, array(), true)) {
            $this->error = $model->getError();
            return false;
        }
        return true;
    }

    /**
     * 清除会话 强制下线
     * @param $uid
     * @param string $type
     * @return mixed
     */
    public function clearSession($uid, $type = 'login')
    {
        $map = array(
            'uid' => $uid,
            'type' => $type,
        );
        return D('UserSession')->where($map)->delete();
    }

    /**
     * 根据配置验证用户名
     * @param $username
     * @return false|int
     */
    protected function checkUsername($username)
    {
        $regex = C('USER_USERNAME_REGEX');
        return preg_match($regex, $username);
    }

    /**
     * 根据配置验证手机号码
     * @param $phone
     * @return false|int
     */
    protected function checkPhone($phone)
    {
        $regex = C('USER_PHONE_REGEX');
        return preg_match($regex, $phone);
    }

    protected function _before_write(&$data)
    {
        parent::_before_write($data);
        if ($data['password']) {
            $data['password'] = $this->pwdHash($data['password']);
        } else if (isset($data['password']) && !$data['password']) {
            unset($data['password']);
        }
    }

    protected function _after_find(&$result, $options)
    {
        parent::_after_find($result, $options);
        if ($this->num2text) {
            foreach ($this->num2text as $key => $v) {
                if (isset($result[$key])) {
                    $result[$key . '_text'] = $v[$result[$key]];
                }
            }
        }
        if ($result['avatar']) {
            $result['avatar_format'] = $this->crop_format($result['avatar']);
        }
    }

    protected function _after_select(&$result, $options)
    {
        parent::_after_select($result, $options);
        foreach ($result as &$row) {
            $this->_after_find($row, $options);
        }
    }

    protected function _before_delete($options)
    {
        parent::_before_delete($options);
        if ($options['where']) {
            $this->delete_list = $this->where($options['where'])->select();
        }
    }

    protected function _before_update(&$data, $options)
    {
        parent::_before_update($data, $options);
        if ($options['where']) {
            $this->update_list = $this->where($options['where'])->select();
        }
    }

    protected function _after_delete($data, $options)
    {
        parent::_after_delete($data, $options);
        foreach ($this->delete_list as $item) {
            if ($item['avatar'] && is_file($item['avatar'])) {
                @unlink($item['avatar']);
            }
        }
    }

    protected function _after_update($data, $options)
    {
        parent::_after_update($data, $options);
        foreach ($this->update_list as $item) {
            if (isset($data['avatar'])) {
                $this->compare2del($data['avatar'], $item['avatar']);
            }
        }
    }

    /**
     * 根据配置指定用户状态
     * @return integer 用户状态
     */
    protected function getStatus()
    {
        return true; //TODO: 暂不限制，下一个版本完善
    }

    /**
     * 检测用户名是不是被禁止注册
     * @param  string $username 用户名
     * @return boolean          ture - 未禁用，false - 禁止注册
     */
    protected function checkDenyMember($username)
    {
        return true; //TODO: 暂不限制，下一个版本完善
    }

    /**
     * 检测邮箱是不是被禁止注册
     * @param  string $email 邮箱
     * @return boolean       ture - 未禁用，false - 禁止注册
     */
    protected function checkDenyEmail($email)
    {
        return true; //TODO: 暂不限制，下一个版本完善
    }

    /**
     * 检测手机是不是被禁止注册
     * @param  string $mobile 手机
     * @return boolean        ture - 未禁用，false - 禁止注册
     */
    protected function checkDenyMobile($mobile)
    {
        return true; //TODO: 暂不限制，下一个版本完善
    }

    /**
     * 检测Ip是不是被禁止注册
     * @param  string $mobile 手机
     * @return boolean        ture - 未禁用，false - 禁止注册
     */
    protected function checkDenyIp($ip)
    {
        return true; //TODO: 暂不限制，下一个版本完善
    }
}